using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class LevelGenerator : MonoBehaviour 
{
	static public readonly int sMaxTime = 30;
	
	static public float sTime;
	static public float sSpeed = 60.0f;
	
	static public float sSpeedIncrementPerSecond = 6.0f;
	static public float sMaxSpeed = 120.0f;


	public int BonusDegatSpawn = 10;
	public int BonusTempsSpawn = 10;
	public int BonusBombSpawn = 10;
	public int BonusShieldSpawn = 10;

	public float palierPowerUp1 = 10;
	public float palierPowerUp2 = 10;
	public float palierPowerUp3 = 10;

	public float palierPowerUp1Value = 1.5f;
	public float palierPowerUp2Value = 2.25f;
	public float palierPowerUp3Value = 3.37f;

	public float PowerUpMinTime = 5;
	public float PowerUpMaxTime = 10;

	
	#region Variables
	
	
	public float Z;
	
	public GameObject BlocPrefab;
	
	public GameObject MovingWindow;
	public GameObject TopMovingWindow;
	public GameObject BottomMovingWindow;
	
	public GameObject EnnemyPrefab;
	
	public GameObject PowerUpPrefab;
	
	//The size of a bloc for it to fill the screen.
	public Vector2 BlocScreenSize;
	
	public List<GameObject> EnnemyList;
	
	
	public float SpawningStep;
	public float TimeToEnd;
	
	
	//*****************
	
	//The Rect that define the actual spawning rect
	private Vector3 mTopSpawningWindow;
	private Vector3 mBottomSpawningWindow;
	
	private Vector3 mTopBasPos;
	
	private GameObject mLastSpawned = null;
	private Vector3 mLastSpawnedPos;
	
	private BlocAnimator TopAnimator;
	private BlocAnimator BottomAnimator;
	
	private Range mWalkablePos;
	
	private List<GameObject> mUsableEnnemy;
	
	private float mStartingTime;
	
	private float mLastStep;
	private int mSimultaneousSpawning;
	
	#endregion
	
	#region Methods
	
	// Use this for initialization
	void Start () 
	{
		Time.timeScale = 0.8f;
		
		sTime = sMaxTime;
		
		TopAnimator = TopMovingWindow.GetComponent<BlocAnimator>();
		BottomAnimator = BottomMovingWindow.GetComponent<BlocAnimator>();
		
		TopAnimator.enabled = false;
		BottomAnimator.enabled = false;
		
		mTopBasPos = TopMovingWindow.transform.position;
		
		
		mTopSpawningWindow = TopMovingWindow.transform.position;
		mBottomSpawningWindow = BottomMovingWindow.transform.position;
		
		ComputeWalkable();
		
		SpawnNewBloc();
		
		mUsableEnnemy = new List<GameObject>();
		mStartingTime = Time.time;
		
		mLastStep = 0;
		mSimultaneousSpawning = 0;
		
		StartCoroutine(SpawningCoroutine(Random.Range(2.0f, 5.0f)));
		StartCoroutine(SpawnEnnemyCoroutine(Random.Range(2.0f, 5.0f)));
		StartCoroutine(SpawnBonus(Random.Range(PowerUpMinTime, PowerUpMaxTime)));
	}
	
	// Update is called once per frame
	void Update () 
	{
		if(sTime > sMaxTime)
			sTime = sMaxTime;
		else if (sTime < 0)
			sTime = 0;
		
		mLastStep += Time.deltaTime;
		
		if(sSpeed < sMaxSpeed)
		{
			sSpeed = Mathf.Min(sSpeed + sSpeedIncrementPerSecond, sMaxSpeed);
		}
		
		sTime -= Time.deltaTime;
		
		MovingWindow.transform.position = new Vector3(MovingWindow.transform.position.x,
		                                              (-BlocScreenSize.y * 0.1f) + Mathf.PingPong(Time.time, 1) * (BlocScreenSize.y * 0.1f), 
		                                              Z);
		
		TopMovingWindow.transform.position = new Vector3(MovingWindow.transform.position.x,
		                              mTopBasPos.y - (Time.time - mStartingTime)/TimeToEnd * 80,
		                              Z);
		
		if((mLastSpawnedPos - mLastSpawned.transform.position).sqrMagnitude
		   >= (mLastSpawned.collider.bounds.size.x - 15) * (mLastSpawned.collider.bounds.size.x - 15))
			SpawnNewBloc();
	}
	
	//**************************************************************************************************
	
	public void SpawnNewBloc()
	{
		float X;
		
		if(mLastSpawned != null)
			X = mLastSpawned.transform.position.x + (mLastSpawned.collider.bounds.size.x - 15);
		else
			X = mTopSpawningWindow.x;
		
		TopAnimator.enabled = true;
		mLastSpawned = Instantiate(TopMovingWindow, new Vector3(X, mTopSpawningWindow.y, mTopSpawningWindow.z), TopMovingWindow.transform.rotation) as GameObject;
		//mLastSpawned.AddComponent<BlocAnimator>();
		mLastSpawnedPos = mLastSpawned.transform.position;
		TopAnimator.enabled =false;
		
		
		BottomAnimator.enabled = true;
		mLastSpawned = Instantiate(BottomMovingWindow, new Vector3(X, mBottomSpawningWindow.y, mBottomSpawningWindow.z), BottomMovingWindow.transform.rotation) as GameObject;
		//mLastSpawned.AddComponent<BlocAnimator>();
		mLastSpawnedPos = mLastSpawned.transform.position;
		BottomAnimator.enabled = false;
		
		if((mBottomSpawningWindow - mLastSpawned.transform.position).sqrMagnitude
		   >= (mLastSpawned.collider.bounds.size.x - 15) * (mLastSpawned.collider.bounds.size.x - 15))
			SpawnNewBloc();
	}
	
	//********************************************************************************
	
	private void ComputeWalkable()
	{
		mWalkablePos = new Range(mTopSpawningWindow.y - TopMovingWindow.renderer.bounds.extents.y,
		                         mBottomSpawningWindow.y + BottomMovingWindow.renderer.bounds.extents.y);
	}
	
	//********************************************************************************
	
	/// <summary>
	/// Coroutine that wait for pSeconds then change the pos of the spawning window to the current pos of the moving window.
	/// </summary>
	/// <param name="pSeconds">
	/// The time in second before next change
	/// </param>
	IEnumerator SpawningCoroutine(float pSeconds)
	{
		yield return new WaitForSeconds(pSeconds);
		
		mTopSpawningWindow = TopMovingWindow.transform.position;
		mBottomSpawningWindow = BottomMovingWindow.transform.position;
		
		ComputeWalkable();
		
		StartCoroutine(SpawningCoroutine(Random.Range(2.0f, 5.0f)));
	}
	
	//*********************************************************************************************************
	
	IEnumerator SpawnEnnemyCoroutine(float pSeconds)
	{
		yield return new WaitForSeconds(pSeconds);
		
		for(int i = 0; i < EnnemyList.Count; i++)
		{
			Ennemy lScr = EnnemyList[i].GetComponent<Ennemy>();
			
			if(lScr.StartingTime < Time.time - mStartingTime)
			{
				mUsableEnnemy.Add(EnnemyList[i]);
				EnnemyList.RemoveAt(i);
			}
		}
		
		int lEnnemyNum = Random.Range(0, mUsableEnnemy.Count);
		
		float y = mWalkablePos.Bottom + ((mWalkablePos.Top - mWalkablePos.Bottom) * 0.5f);
		
		GameObject lObj = Instantiate(mUsableEnnemy[lEnnemyNum], new Vector3(mTopSpawningWindow.x, y, Z), mUsableEnnemy[lEnnemyNum].transform.rotation) as GameObject;
		lObj.GetComponent<Ennemy>().Spawned(mWalkablePos);
		
		StartCoroutine(SpawnEnnemyCoroutine(Random.Range(2.0f, 4.0f)));
		
		if(mLastStep >= SpawningStep && mSimultaneousSpawning < 2)
		{
			mLastStep = 0;
			mSimultaneousSpawning++;
			
			StartCoroutine(SpawnEnnemyCoroutine(Random.Range(2.0f, 4.0f)));
		}
	}
	
	//****************************************************************************************************
	
	IEnumerator SpawnBonus(float pSeconds)
	{
		yield return new WaitForSeconds(pSeconds);
		
		float y = mWalkablePos.Bottom + ((mWalkablePos.Top - mWalkablePos.Bottom) * 0.5f);
		GameObject lObj = Instantiate(PowerUpPrefab, new Vector3(mTopSpawningWindow.x, y, Z), PowerUpPrefab.transform.rotation) as GameObject;
		
		int maxSpawn = BonusDegatSpawn + BonusTempsSpawn + BonusBombSpawn + BonusShieldSpawn;

		int lType = Random.Range(1, maxSpawn);
		if (lType < BonusDegatSpawn)
		{
			lType = 1;
		}
		else if (lType < BonusDegatSpawn + BonusTempsSpawn)
		{
			lType = 3;
		}
		else if (lType < BonusDegatSpawn + BonusTempsSpawn + BonusBombSpawn)
		{
			lType = 2;
		}
		else
		{
			lType = 4;
		}

		lObj.GetComponent<PowerUp>().Type = lType;
		float facteur = 0;

		if (sTime > palierPowerUp1 + palierPowerUp2 + palierPowerUp3)
		{
			facteur = palierPowerUp3Value;
		}
		else if (sTime > palierPowerUp1 + palierPowerUp2)
		{
			facteur = palierPowerUp2Value;
		}
		else if (sTime > palierPowerUp1)
		{
			facteur = palierPowerUp1Value;
		}
		else
		{
			facteur = 1;
		}

		StartCoroutine(SpawnBonus(Random.Range(PowerUpMinTime * facteur, PowerUpMaxTime * facteur)));
	}
	
	#endregion
}


public class Range
{
	public float Top;
	public float Bottom;
	
	public Range(float pTop, float pBottom)
	{
		Top = pTop;
		Bottom = pBottom;
	}
	
	public override string ToString ()
	{
		return "Top:"+Top+"; Bottom:"+Bottom;
	}
}
